#include "stm32f1xx.h"
#include "lfs.h"
#include "lift_info.h"
#include "board_config.h"

#define STM32_FLASH_SIZE    64      //所选STM32的FLASH容量大小(单位为K)

#if STM32_FLASH_SIZE<256
#define STM32_SECTOR_SIZE   1024    //字节
#else 
#define STM32_SECTOR_SIZE	2048
#endif

#define STM32_FLASH_BASE    0x08000000

#define FS_BASE_PAGE_START  60
#define FS_BASE_ADDR        (STM32_FLASH_BASE + FS_BASE_PAGE_START * STM32_SECTOR_SIZE)

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(__array) (sizeof(__array) / sizeof(__array[0]))
#endif

lfs_t lfs;
lfs_file_t file;

int stm32_interl_flash_block_read(const struct lfs_config *c, lfs_block_t block,
            lfs_off_t off, void *buffer, lfs_size_t size)
{
    uint32_t src_addr = FS_BASE_ADDR + c->block_size*block +off;
    uint32_t read_size = 0;
    uint16_t* pDest = (uint16_t*)buffer;
    uint16_t* pSrc = (uint16_t*)src_addr;
    while(read_size < size){
        *pDest = *(pSrc);
        pDest++;
        pSrc++;
        read_size += 2;
    }

    return 0;
}            

int stm32_interl_flash_block_prog(const struct lfs_config *c, lfs_block_t block,
            lfs_off_t off, const void *buffer, lfs_size_t size)
{
    HAL_FLASH_Unlock();
    uint32_t  dest_addr = FS_BASE_ADDR + c->block_size*block +off;
    uint64_t *pSrc = (uint64_t*)buffer;
    uint32_t  write_size = 0;
    while(write_size < size){
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,dest_addr,*(pSrc)) != HAL_OK){
            HAL_FLASH_Lock();
            return HAL_FLASH_GetError();
        }
        pSrc++;
        dest_addr += 8;
        write_size += 8;
    }
    HAL_FLASH_Lock();
    return 0;
}

int stm32_interl_flash_block_erase(const struct lfs_config *c, lfs_block_t block)
{
    static FLASH_EraseInitTypeDef EraseInitStruct;
    uint32_t PageError;
    HAL_FLASH_Unlock();

    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    EraseInitStruct.Banks = FLASH_BANK_1;
    EraseInitStruct.PageAddress = FS_BASE_ADDR + block * STM32_SECTOR_SIZE;
    EraseInitStruct.NbPages = 1;
    
    if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError)!= HAL_OK){
        return HAL_FLASH_GetError();
    }

    HAL_FLASH_Lock();
    return 0;
}

int stm32_interl_flash_block_sync(const struct lfs_config *c)
{
    return 0;
}

#define LFS_BLOCK_COUNT     4
#define LFS_CACHE_SIZE      256
#define LFS_LOOKAHEAD_SIZE  16

static uint8_t lfs_read_buffer[LFS_CACHE_SIZE] = {0};
static uint8_t lfs_write_buffer[LFS_CACHE_SIZE] = {0};
static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE] = {0};

static uint8_t lfs_file_buffer[LFS_CACHE_SIZE] = {0};

const struct lfs_config cfg = {
    .read  = stm32_interl_flash_block_read,
    .prog  = stm32_interl_flash_block_prog,
    .erase = stm32_interl_flash_block_erase,
    .sync  = stm32_interl_flash_block_sync,

    .read_size = 8,
    .prog_size = 8,
    .block_size = STM32_SECTOR_SIZE,
    .block_count = LFS_BLOCK_COUNT,
    .cache_size = LFS_CACHE_SIZE,
    .lookahead_size = LFS_LOOKAHEAD_SIZE,
    .block_cycles = 500,
    .read_buffer = lfs_read_buffer,
    .prog_buffer = lfs_write_buffer,
    .lookahead_buffer = lfs_lookahead_buffer,
};

lfs_t lfs;
lfs_file_t file;
struct lfs_file_config file_cfg = {
    .buffer = lfs_file_buffer,
    .attr_count = 0,
};

struct board_config {
    uint8_t addr;
    uint16_t value;
    uint16_t default_value;
    uint16_t min;
    uint16_t max;
};

static volatile struct board_config board_config[] = {
    [CONFIG_SPECIFIED_SPEED] = {
        .addr = CONFIG_SPECIFIED_SPEED * 8,
        .default_value = 40,
        .value = 40,
        .min = 10,
        .max = 80,
    },
    [CONFIG_TOTAL_FLOOR] = {
        .addr = CONFIG_TOTAL_FLOOR * 8,
        .default_value = 0,
        .value = 0,
        .min = 2,
        .max = 5,
    },
    [CONFIG_LEARNING_OF_FLOOR_HEIGHT] = {
        .addr = CONFIG_LEARNING_OF_FLOOR_HEIGHT * 8,
        .default_value = 0,
        .value = 0,
        .min = 1,
        .max = 5,
    },
    [CONFIG_TOP_FORCE_POS] = {
        .addr = CONFIG_TOP_FORCE_POS * 8,
        .default_value = 250,
        .value = 250,
        .min = 200,
        .max = 500,
    },
    [CONFIG_BUTTOM_FORCE_POS] = {
        .addr = CONFIG_BUTTOM_FORCE_POS * 8,
        .default_value = 250,
        .value = 250,
        .min = 200,
        .max = 500,
    },
    [CONFIG_TOP_FLAT_LAYER] = {
        .addr = CONFIG_TOP_FLAT_LAYER * 8,
        .default_value = 30,
        .value = 30,
        .min = 0,
        .max = 500,
    },
    [CONFIG_BUTTOM_FLAT_LAYER] = {
        .addr = CONFIG_BUTTOM_FLAT_LAYER * 8,
        .default_value = 30,
        .value = 30,
        .min = 0,
        .max = 500,
    },
    [CONFIG_TOP_TRIMMING_POS] = {
        .addr = CONFIG_TOP_TRIMMING_POS * 8,
        .default_value = 1,
        .value = DEFAULT_TOP_TRIMMING_POS,
        .min = DEFAULT_TOP_TRIMMING_POS_MIN,
        .max = DEFAULT_TOP_TRIMMING_POS_MAX,
    },
    [CONFIG_DOWN_TRIMMING_POS] = {
        .addr = CONFIG_DOWN_TRIMMING_POS * 8,
        .default_value = 1,
        .value = DEFAULT_TOP_TRIMMING_POS,
        .min = DEFAULT_TOP_TRIMMING_POS_MIN,
        .max = DEFAULT_TOP_TRIMMING_POS_MAX,
    },
    [CONFIG_COVER_ENABLE] = {
        .addr = CONFIG_COVER_ENABLE * 8,
        .default_value = 0,
        .value = 0,
        .min = 0,
        .max = 1,
    },
};

static void lfs_config_write(int32_t addr, uint16_t value)
{
    lfs_file_seek(&lfs, &file, addr, LFS_SEEK_SET);
    lfs_file_write(&lfs, &file, &value, sizeof(value));
}

static uint16_t lfs_config_read(int32_t addr)
{
    uint16_t temp = 0;
    lfs_file_seek(&lfs, &file, addr, LFS_SEEK_SET);
    lfs_file_read(&lfs, &file, &temp, sizeof(temp));
    
    return temp;
}

static void lfs_config_write_u32(int32_t addr, uint32_t value)
{
    lfs_file_seek(&lfs, &file, addr, LFS_SEEK_SET);
    lfs_file_write(&lfs, &file, &value, sizeof(value));
}

static uint32_t lfs_config_read_u32(int32_t addr)
{
    uint32_t temp = 0;
    lfs_file_seek(&lfs, &file, addr, LFS_SEEK_SET);
    lfs_file_read(&lfs, &file, &temp, sizeof(temp));
    
    return temp;
}

void board_config_set_item_value(enum board_config_e item, uint16_t value)
{
    board_config[item].value = value;
}

static void board_config_open(void)
{
    lfs_file_opencfg(&lfs, &file, "board_config", LFS_O_RDWR | LFS_O_CREAT, &file_cfg);
}

static void board_config_close(void)
{
    lfs_file_close(&lfs, &file);
}

void board_config_write(enum board_config_e item, uint16_t value)
{
    board_config[item].value = value;

    board_config_open();
    
    lfs_file_seek(&lfs, &file, board_config[item].addr, LFS_SEEK_SET);
    lfs_file_write(&lfs, &file, &value, sizeof(value));
    
    board_config_close();
}

void board_config_floor_info_write(uint16_t base_addr, uint8_t floor, uint32_t value)
{
    if (floor >= MAX_TOTAL_FLOOR) {
        return;
    }

    board_config_open();

    lfs_file_seek(&lfs, &file, base_addr + 8 * floor, LFS_SEEK_SET);
    lfs_file_write(&lfs, &file, &value, sizeof(value));

    board_config_close();
}

uint16_t board_config_adjust(enum board_config_e item, int8_t adjust)
{
    static uint16_t temp = 0;

    if (item == 10 || item == 11) {
        return 0;
    }

    if (adjust == 0) {
        if (item == CONFIG_TOP_TRIMMING_POS) {
            temp = lift.floor_info[lift.current_floor].up_flat_trimming_pos;
        } else if (item == CONFIG_DOWN_TRIMMING_POS) {
            temp = lift.floor_info[lift.current_floor].down_flat_trimming_pos;
        } else {
            temp = board_config[item].value;
        }
    } else if (adjust > 0) {
        if (temp < board_config[item].max) {
            temp += adjust;
            if (temp > board_config[item].max) {
                temp = board_config[item].max;
            }
        }
    } else {
        if (temp > board_config[item].min) {
            temp += adjust;
            if ((int16_t)temp < board_config[item].min) {
                temp = board_config[item].min;
            }
        }
    }

    return temp;
}

uint16_t board_config_password_adjust(int8_t adjust)
{
    static uint16_t temp = 0;

    if (adjust == 0) {
        temp = 0;
    } else if (adjust > 0) {
        if (temp < 999) {
            temp += adjust;
            if (temp > 999) {
                temp = 999;
            }
        }
    } else {
        if (temp > 0) {
            temp += adjust;
            if ((int16_t)temp < 0) {
                temp = 0;
            }
        }
    }

    return temp;
}

int board_config_init(void)
{
    int err = lfs_mount(&lfs, &cfg);

    if (err) {
        lfs_format(&lfs, &cfg);
        lfs_mount(&lfs, &cfg);

        lfs_file_opencfg(&lfs, &file, "board_config", LFS_O_RDWR | LFS_O_CREAT, &file_cfg);
        for (int i = 0; i < ARRAY_SIZE(board_config); i++) {
            lfs_config_write(board_config[i].addr, board_config[i].default_value);
        }
        for (int i = 0; i < MAX_TOTAL_FLOOR; i++) {
            lfs_config_write_u32(CONFIG_FLAT_POS_ADDR + 8 * i, 0);
            lfs_config_write(CONFIG_UP_TRIMMING_POS_ADDR + 8 * i, DEFAULT_TOP_TRIMMING_POS);
            lfs_config_write(CONFIG_DOWN_TRIMMING_POS_ADDR + 8 * i, DEFAULT_DOWN_TRIMMING_POS);
            lfs_config_write_u32(CONFIG_UP_SLOW_DOWN_POS_ADDR + 8 * i, 0);
            lfs_config_write_u32(CONFIG_DOWN_SLOW_DOWN_POS_ADDR + 8 * i, 0);
        }
        lfs_file_close(&lfs, &file);
    }

    lfs_file_opencfg(&lfs, &file, "board_config", LFS_O_RDWR | LFS_O_CREAT, &file_cfg);

    for (int i = 0; i < ARRAY_SIZE(board_config); i++) {
        uint16_t temp = lfs_config_read(board_config[i].addr);
        if (temp != board_config[i].default_value) {
            board_config[i].value = temp;
        }
    }

    for (int i = 0; i < MAX_TOTAL_FLOOR; i++) {
        lift.floor_info[i].flat_pos = lfs_config_read_u32(CONFIG_FLAT_POS_ADDR + 8 * i);

        lift.floor_info[i].up_flat_trimming_pos = lfs_config_read(CONFIG_UP_TRIMMING_POS_ADDR + 8 * i);
        lift.floor_info[i].down_flat_trimming_pos = lfs_config_read(CONFIG_DOWN_TRIMMING_POS_ADDR + 8 * i);

        lift.floor_info[i].up_slow_down_pos = lfs_config_read_u32(CONFIG_UP_SLOW_DOWN_POS_ADDR + 8 * i);
        lift.floor_info[i].down_slow_down_pos = lfs_config_read_u32(CONFIG_DOWN_SLOW_DOWN_POS_ADDR + 8 * i);
    }

    lfs_file_close(&lfs, &file);

    lift.total_floor = board_config[CONFIG_TOTAL_FLOOR].value;
    lift.is_cover_enable = false;
    board_config_set_item_value(CONFIG_COVER_ENABLE, false);
    lift.specified_speed = board_config[CONFIG_SPECIFIED_SPEED].value;
    lift.password = 123;
    lift.hardware_test = 246;

    lift.up_slow_down_pos = board_config[CONFIG_TOP_FORCE_POS].value;
    lift.down_slow_down_pos = board_config[CONFIG_BUTTOM_FORCE_POS].value;

    lift.up_flat_pos = board_config[CONFIG_TOP_FLAT_LAYER].value;
    lift.down_flat_pos = board_config[CONFIG_BUTTOM_FLAT_LAYER].value;

    for (int i = 0; i < MAX_TOTAL_FLOOR; i++) {
        if (i >= lift.total_floor) {
            break;
        }

        if (lift.floor_info[i].flat_pos == 0) {
            continue;
        }

        lift.floor_info[i].up_flat_pos = lift.floor_info[i].flat_pos + lift.up_flat_pos;
        lift.floor_info[i].up_flat_pos += ((int)lift.floor_info[i].up_flat_trimming_pos - 50);

        lift.floor_info[i].down_flat_pos = lift.floor_info[i].flat_pos - lift.down_flat_pos;
        lift.floor_info[i].down_flat_pos += ((int)lift.floor_info[i].down_flat_trimming_pos - 50);
    }

    if (lift.total_floor > 0) {
        lift.sul_pos = lift.floor_info[lift.total_floor - 1].flat_pos + DEFUALT_SUL_HEIGHT_THAN_TOP;
        lift.max_height_pos = lift.floor_info[lift.total_floor - 1].flat_pos + DEFUALT_MAX_HEIGHT_THAN_TOP;
    }

    lift.is_floor_info_learned = lift_is_floor_info_learned();

    return 0;
}


